在昨天我們度過最大難關加密了,之後應該會輕鬆許多吧?
API 呼叫流程如下
步驟 1, 2, 3 目前都是我們已經會的!
第 4 步驟的 API Request 需求如下
快速說明這六個參數
Version
: 固定值 1.0.0
ShopNo
: 固定值 根據永豐給你的編號輸入APIService
: 有三種變化,OrderCreate
(建立訂單)、OrderQuery
(訂單詳情)、OrderPayQuery
(訂單付款資訊)Nonce
: 利用 ShopNo
對 API 請求一個隨機值Sign
: 訂單內容的簽章,避免傳送過程被竄改Message
: 加密過後的訂單內容我們今天會聚集在 API Service 的 OrderCreate 上面
我以信用卡為例,訂單內容如下
{
"ShopNo": shop_no, ## 商家編號
"OrderNo": "A202109150001", ## 訂單編號 (商家自行產生)
"Amount": 50000, ## 金額,最後兩位為小數 (輸入50000,實際上付款為 500.00)
"CurrencyID": "TWD", ## 幣別,目前只有台幣這個選項
"PayType": "A", ## A 代表 ATM 轉帳
"CardParam": { ## 信用卡的參數
"AutoBilling": "Y", ## 自動請款
"ExpBillingDays": "", ## 自動請款天數
"ExpMinutes": "", ## 付款連結有效時間
"PayTypeSub": "" ## 付款子項目 (一次付清)
},
"PrdtName": "OrderCreate_CreditCard_Example", ## 產品名稱
"ReturnURL": "<URL>", ## 付款成功時 redirect 的網頁
"BackendURL": "<URL>" ## 訂單完成付款時,會通知到這裡
}
只要把上面的資訊加密後輸入到下面 Message 欄位,就可以發出 Query 了!
{
'Version': '1.0.0',
'ShopNo': shop_no,
'APIService': 'OrderCreate',
'Sign': sign,
'Nonce': nonce,
'Message': msg
}
API 回傳的內容也跟 上面一樣,差別為內容是永豐要跟你說的訊息
程式碼非常簡單,東西填一填就可以發出 Request 了
def apiService(service, sign, nonce, msg):
api_data = {
'Version': '1.0.0',
'ShopNo': shop_no,
'APIService': service,
'Sign': sign,
'Nonce': nonce,
'Message': msg
}
r = requests.post(order_url, json = api_data)
resp = json.loads(r.content)
return (resp['Sign'], resp['Nonce'], resp['Message'])
resp_sign, resp_nonce, resp_message = apiService('OrderCreate', signature, nonce, enc_message)
那我們今天就說到這裡,明天會說明其他兩個 API Service!
一樣在最後附上程式碼~
from __future__ import unicode_literals
import requests
import hashlib
import codecs
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
shop_no = '<shop_no>'
sinopac_hash = {
'a1': '',
'a2': '',
'b1': '',
'b2': ''
}
nonce_url = 'https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Nonce'
order_url = 'https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order'
def getNonce():
nonce_data = {
'ShopNo': shop_no
}
r = requests.post(nonce_url, json=nonce_data)
return json.loads(r.content)['Nonce']
def calcHashID():
a1 = sinopac_hash['a1']
a2 = sinopac_hash['a2']
b1 = sinopac_hash['b1']
b2 = sinopac_hash['b2']
xor1 = hex(int(a1, base=16)^int(a2, base=16))
xor2 = hex(int(b1, base=16)^int(b2, base=16))
hash_id = xor1[2:]+xor2[2:]
return hash_id.upper()
def calcIV(nonce):
s = hashlib.sha256()
s.update(nonce.encode('utf-8'))
h = s.hexdigest()
return h[-16:].upper()
def calcSign(msg_content, nonce, hash_id):
sign_msg = msg_content+nonce+hash_id
s = hashlib.sha256()
s.update(sign_msg.encode('utf-8'))
h = s.hexdigest()
return h.upper()
def parseQueryData(msg_param):
if type(msg_param) != dict:
return
order_message = dict(sorted(msg_param.items(), key = lambda x: x[0]))
message = ''
for k, v in order_message.items():
if type(v) == dict or v == '':
continue
message += f"{k}={v}&"
return message[:-1]
def CBCEncrypt(key, iv, data):
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
return (cipher.encrypt(pad(data.encode('utf-8'), AES.block_size)))
def CBCDecrypt(key, iv, data):
data = codecs.decode(data, "hex")
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
return unpad(cipher.decrypt(data), AES.block_size)
def apiService(service, sign, nonce, msg):
api_data = {
'Version': '1.0.0',
'ShopNo': shop_no,
'APIService': service,
'Sign': sign,
'Nonce': nonce,
'Message': msg
}
r = requests.post(order_url, json = api_data)
resp = json.loads(r.content)
return (resp['Sign'], resp['Nonce'], resp['Message'])
def orderCreate(order_create):
nonce = getNonce()
hash_id = calcHashID()
iv = calcIV(nonce)
content = parseQueryData(order_create)
signature = calcSign(content, nonce, hash_id)
msg = json.dumps(order_create, ensure_ascii=False).replace(' ','')
enc_message = CBCEncrypt(hash_id, iv, msg).hex().upper()
resp_sign, resp_nonce, resp_message = apiService('OrderCreate', signature, nonce, enc_message)
resp_iv = calcIV(resp_nonce)
temp = CBCDecrypt(hash_id, resp_iv, resp_message)
print(json.loads(temp))
order_create = {
"ShopNo": shop_no,
"OrderNo": "A202109150006",
"Amount": 51000,
"CurrencyID": "TWD",
"PayType": "C",
"CardParam": {
"AutoBilling": "Y"
},
"PrdtName": "OrderCreate_CreditCard_Example",
"ReturnURL": "http://10.11.22.113:8803/QPay.ApiClient/Store/Return",
"BackendURL": "http://10.11.22.113:8803/QPay.ApiClient/AutoPush/PushSuccess"
}
orderCreate(order_create)